/*
 * HD44780.c
 *
 *    Modified: Aug 10, 2009 Orlando Arias
 *              Separated send function from core library.
 *	  Modified: Aug 02, 2009 Orlando Arias
 *	  			Added command structure.
 *	  Modified: Aug 01, 2009 Orlando Arias
 *	  			Modified API to use lcd_ prefixes.
 *	  Modified: Jul 21, 2009 Rugged Circuits LLC
 *				Modified to use pointers to LCD structure
 *  Created on: Jul 20, 2009
 *      Author: Orlando Arias
 *     License: GPLv3
 *
 *   Simple library to control HD44780 based LCD screens.
 *   Copyright (C) 2009  Orlando Arias
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <inttypes.h>
#include "WProgram.h"
#include "HD44780.h"
#include "hd44780_private.h"


void lcd_init(LCD *l){
	// initialize pinout
	for (int i = 0; i < ((l->bitmode == BIT_MODE_8) ? 8:4);i++)
		pinMode(l->datapin[i], OUTPUT);

	pinMode(l->rs_pin, OUTPUT);
	pinMode(l->rw_pin, OUTPUT);
	pinMode(l->enable_pin, OUTPUT);

	/*
	 * Since it is unsafe to assume the LCD is ready to go when the Arduino
	 * is powered, the following commands are run.
	 *
	 * function set: 8 bits, 1 line, 5x8 dots if BIT_MODE_8 (0x38) or
	 * 				 4 bits, 1 line, 5x8 dots if BIT_MODE_4 (0x28)
	 * display control: turn display on, cursor off, no blinking (0x0C)
	 * entry mode set: increment automatically, display
	 * 				   shift, right shift (0x06)
	 */
	lcd_sendCommand(l, (l->bitmode == BIT_MODE_8) ? 0x38:0x28);
	lcd_sendCommand(l, CURSOR_INVISIBLE);
	lcd_sendCommand(l, EM_INCREMENT_OFF);
	lcd_clear(l);
}

void lcd_clear(LCD *l){
	// clear display, set cursor position to zero
	lcd_sendCommand(l, CLEAR);
	lcd_sendCommand(l, MOVE_HOME);
}

void lcd_home(LCD *l){
	// set cursor position to zero
	lcd_sendCommand(l, MOVE_HOME);
}

void lcd_locate(LCD *l, int row, int col){
	// set cursor position
	int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
	lcd_sendCommand(l, SET_POSITION | (col + row_offsets[row]));
}

void lcd_sendCommand(LCD *l, command cmd){
	lcd_sendRaw(l, cmd, LOW);
	while (lcd_readState(l) == BUSY); // delay until command is executed.
}

void lcd_writeChar(LCD *l, uint8_t ch){
	lcd_sendRaw(l, ch, HIGH);
}

void lcd_writeStr(LCD *l, const char str[]){
	lcd_parseStr(l, str);
}

state lcd_readState(LCD *l){
	digitalWrite(l->rw_pin, HIGH);
	digitalWrite(l->rs_pin, LOW);
	digitalWrite(l->enable_pin, HIGH);
	pinMode(l->datapin[l->bitmode - 1], INPUT);
	state s = ((digitalRead(l->datapin[l->bitmode - 1]) == LOW)? READY : BUSY);
	pinMode(l->datapin[l->bitmode - 1], OUTPUT);
	return s;
}
